#include "server.h"

#define MONITOR_IDLE_TIMEOUT    SECOND_OF_MINUTE(5) /* 监控闲置超时 */

static unsigned int quit=IS_FALSE;

typedef int (*P_CMD_FUNC)(char *, char *);
#define CMD_FUNC(cmd)   \
int CMD_FUNC_##cmd(char *request, char *respond)

static CMD_FUNC(RELOADPUBKEY);

static CMD_FUNC(HELP);
static CMD_FUNC(SHUTDOWN);
static CMD_FUNC(PRINTCLIENT);
static CMD_FUNC(PRINTSERVICE);

static struct _cmd_map
{/*{{{*/
    char *name;
    char *ab;
    P_CMD_FUNC p_func;
    char *desc;
    char *usage;
}/*}}}*/
cmd_map[]=
{/*{{{*/
    {
        "RELOADPUBKEY", "RLPUBKEY", CMD_FUNC_RELOADPUBKEY,
        "reload public key",
        "reloadpubkey(rlpubkey) kkkk"
    },
    {
        "SHUTDOWN", "SHUTDOWN", CMD_FUNC_SHUTDOWN,
        "shutdown server",
        "shutdown"
    },
    {
        "PRINTCLIENT", "PCLT", CMD_FUNC_PRINTCLIENT,
        "print client ip & port",
        "printclient(pclt)"
    },
    {
        "PRINTSERVICE", "PSVC", CMD_FUNC_PRINTSERVICE,
        "print service status",
        "printservice(psvc)"
    },
    {
        "HELP", "HELP", CMD_FUNC_HELP,
        "",
        ""
    }
};/*}}}*/
static unsigned int CMD_MAP_SIZE=
sizeof(cmd_map)/sizeof(struct _cmd_map);

extern int reload_pubkey(char *pubkey_id);

static CMD_FUNC(RELOADPUBKEY)
{/*{{{*/
#define PUBKEY_ID_SIZE          4
    char para_desc[30];
    PARA para;
    char pubkey_id[PUBKEY_ID_SIZE+1];
    int offset=0;
    int res;

    sprintf(para_desc, "pubkey_id(string:%d)", PUBKEY_ID_SIZE);
    res=para_load(request, para_desc, &para);
    if(res)
	{
        if(res > 0)
            errlog("%s: request(%s) no.%d para invalid",
                    __func__, request, res);
        else
            errlog("%s: para_load request(%s) fail, res:%d",
                    __func__, request, res);
        offset+=sprintf(respond+offset,
                "Invalid parameter, type help\n");
		return(offset);
	}
    para_copy_by_name(para, "pubkey_id", pubkey_id);
    para_free(para);

    reload_pubkey(pubkey_id);
    offset+=sprintf(respond+offset, "Reload public key(%s) done\n",
            pubkey_id);
    return(offset);
}/*}}}*/

static CMD_FUNC(HELP)
{/*{{{*/
    struct _cmd_map *map=NULL;
    int offset=0;
    int i;

    // 所有帮助
    if(request)
        while(*request == SPACE)
            request++;
    if(!request || BUF_IS_CLR(request))
    {
        offset+=sprintf(respond+offset, "help [command]\n");
        offset+=sprintf(respond+offset, "commands:\n");
        for(i=1; i<CMD_MAP_SIZE; ++i)
            offset+=sprintf(respond+offset, "    %s\n",
                    cmd_map[i].name);
        return(offset);
    }

    // 单独命令帮助
    for(i=1; i<CMD_MAP_SIZE; ++i)
    {
        map=&cmd_map[i];
        if(strcasecmp(map->name, request) == 0 ||
                strcasecmp(map->ab, request) == 0)
        {
            offset+=sprintf(respond+offset, "Description: %s\n",
                    map->desc);
            offset+=sprintf(respond+offset, "Usage: %s\n",
                    map->usage);
            return(offset);
        }
    }

    offset+=sprintf(respond+offset, "Command not found, type help\n");
    return(offset);
}/*}}}*/

static CMD_FUNC(SHUTDOWN)
{/*{{{*/
#define _TRY_NUM        10
    int offset=0;
    int i, j;

    pthread_mutex_lock(&operate_info._listener_.lock);
    operate_info._listener_.quit=IS_TRUE;
    pthread_mutex_unlock(&operate_info._listener_.lock);
    pthread_mutex_lock(&operate_info._worker_.lock);
    operate_info._worker_.quit=IS_TRUE;
    pthread_mutex_unlock(&operate_info._worker_.lock);
    sleep(1);

    for(i=0; i<_TRY_NUM; ++i)
    {
        pthread_mutex_lock(&operate_info._listener_.lock);
        if(operate_info._listener_.state == THRD_RUNNING)
        {
            pthread_mutex_unlock(&operate_info._listener_.lock);
            goto CONTINUE;
        }
        pthread_mutex_unlock(&operate_info._listener_.lock);

        pthread_mutex_lock(&operate_info._worker_.lock);
        for(j=0; j<cnfg_info.workers; ++j)
        {
            if(operate_info._worker_.state[j] == THRD_RUNNING)
            {
                pthread_mutex_unlock(&operate_info._worker_.lock);
                goto CONTINUE;
            }
        }
        pthread_mutex_unlock(&operate_info._worker_.lock);

        break;

CONTINUE:
        sleep(1);
    }
    if(i == _TRY_NUM)
    {   // 未关闭
        offset+=sprintf(respond+offset,
                "Shutdown not complete, try again");
    }
    else
    {   // 全部关闭
        offset+=sprintf(respond+offset,
                "Shutdown complete, quitting...");
        quit=IS_TRUE;
    }

    return(offset);
}/*}}}*/

static CMD_FUNC(PRINTCLIENT)
{/*{{{*/
    struct _worker_info *worker;
    struct _client_info *client;
    int offset=0;
    int client_num=0;
    int i, j;

    offset+=sprintf(respond+offset,
                        "Client                         Calls\n");
    offset+=sprintf(respond+offset,
            "-------                        -------\n");
    pthread_mutex_lock(&stat_info._worker_.lock);
    for(i=0; i<cnfg_info.workers; ++i)
    {
        worker=&stat_info._worker_.worker[i];
        for(j=0; j<EP_POLL_MAX_NUM; ++j)
        {
            client=&worker->client[j];
            if(client->ip)
            {
                char buf[50];
                sprintf(buf, "%s:%d", client->ip, client->port);
                offset+=sprintf(respond+offset,
                        "%-30s %d\n",
                        buf, client->call_num);
            }
        }
        client_num+=worker->client_num;
    }
    pthread_mutex_unlock(&stat_info._worker_.lock);
    offset+=sprintf(respond+offset,
            "-----------\n"
            "Total: %d",
            client_num);

    return(offset);
}/*}}}*/

static CMD_FUNC(PRINTSERVICE)
{/*{{{*/
    int offset=0;
    struct _svc_info *svc;
    int i;

    offset+=sprintf(respond+offset,
            "Service                        Calls\n");
    offset+=sprintf(respond+offset,
            "-------                        -------\n");
    pthread_mutex_lock(&stat_info._svc_.lock);
    for(i=0; i<SVC_MAP_SIZE; ++i)
    {
        svc=&stat_info._svc_.svc[i];
        offset+=sprintf(respond+offset,
                "%-30s %d\n",
                svc->name, svc->call_num);
    }
    pthread_mutex_unlock(&stat_info._svc_.lock);

    return(offset);
}/*}}}*/

static int _cmd(char *request, char *respond)
{/*{{{*/
    int offset=0;
    struct _cmd_map *map=NULL;
    char *body;
    int cmd_size;

    body=strchr(request, SPACE);
    if(body)
    {
        cmd_size=body-request;
        while(*body == SPACE) body++;
    }
    else
        cmd_size=strlen(request);
    if(cmd_size > 0 && cmd_size <= NAME_SIZE)
    {
        char cmd[NAME_SIZE+1];
        int i;

        strncpy(cmd, request, cmd_size);
        cmd[cmd_size]=ZERO; 
#ifdef _MY_DEBUG
        errlog("%s: cmd(%s)", __func__, cmd);
#endif
        for(i=0; i<CMD_MAP_SIZE; ++i)
        {
            map=&cmd_map[i];
            if(strcasecmp(map->name, cmd) == 0 ||
                    strcasecmp(map->ab, cmd) == 0)
                break;
        }
        if(i == CMD_MAP_SIZE)
        {
            offset+=sprintf(respond+offset, "command(%s) not found",
                    cmd);
            errlog("%s: %s", __func__, respond);
            offset+=sprintf(respond+offset, "\n");
        }
        else
        {
            offset+=map->p_func(body, respond);
        }
    }
    else
    {
        offset+=sprintf(respond+offset, "command invalid");
        errlog("%s: %s", __func__, respond);
        offset+=sprintf(respond+offset, "\n");
    }
    offset+=sprintf(respond+offset, TEXTEND);

    return(offset);
}/*}}}*/

int monitor_start()
{/*{{{*/
#define _TIMEOUT        5
#define _TIMEOUT_MS     (_TIMEOUT*1000) 
    char *request;
    int request_size;
    char *respond;
    int respond_size;
    char buf[LONG_FILE_NM_SIZE+1];
    SKT monitor_skt;
    SKT term_skt;
    int res;
    int timeout_ms;
    int accu_timeout;

    ALLOC_MULTI_DOERR(request, char, BUFFER_SIZE,
            fprintf(stderr, "%s: alloc request fail\n", __func__);
            return(-1));
    ALLOC_MULTI_DOERR(respond, char, SOCK_MSG_SIZE,
            fprintf(stderr, "%s: alloc respond fail\n", __func__);
            return(-1));

    sprintf(buf, "%d", cnfg_info._access_.monitor_port);
    if((res=skt_listen(buf, 0, &monitor_skt)))
    {
        fprintf(stderr, "%s: open monitor port fail\n", __func__);
        return(-1);
    }
    fprintf(stderr, "%s: open monitor port(%d)\n",
            __func__, cnfg_info._access_.monitor_port);

    while(1)
    {
        timeout_ms=_TIMEOUT_MS;
        if((res=skt_accept(monitor_skt, &timeout_ms, &term_skt)))
        {
            if(res != RET_ERR_TIMEOUT)
                errlog("%s: accept fail", __func__);
            continue;
        }
#ifdef _MY_DEBUG
        errlog("%s: console login", __func__);
#endif

        accu_timeout=0;
        while(1)
        {
            timeout_ms=_TIMEOUT_MS;
            if((res=skt_read(term_skt,
                            (unsigned char *)request, &request_size,
                            SKT_R_LINE, &timeout_ms)) < 0)
            {
                if(res == RET_ERR_TIMEOUT)
                {
                    accu_timeout+=_TIMEOUT;
                    if(accu_timeout < MONITOR_IDLE_TIMEOUT)
                        continue;
                    errlog("%s: console timeout, revoke", __func__);
                }
                else
                {
                    if(res == RET_ERR_ABORT)
                        errlog("%s: console revoke", __func__);
                    else
                        errlog("%s: read console fail, res:%d, revoke",
                                __func__, res);
                }
                skt_close(term_skt);
                break;
            }
            if(request[request_size-1] == RTN)
            {
                if(request[request_size-2] == '\r')
                    request[request_size-2]=ZERO;
                else
                    request[request_size-1]=ZERO;
            }
            else
                request[request_size]=ZERO;
            trim_head_space(request);
            trim_tail_space(request);
            accu_timeout=0;

            respond_size=_cmd(request, respond);

            timeout_ms=_TIMEOUT_MS;
            if((res=skt_write(term_skt,
                            (unsigned char *)respond, &respond_size,
                            SKT_W_SIZE, &timeout_ms)) < 0)
            {
                errlog("%s: respond to console fail, res:%d, revoke",
                        __func__, res);
                skt_close(term_skt);
                break;
            }

            // 退出
            if(quit)
            {
                sleep(1);
                skt_close(term_skt);
                goto QUIT;
            }
        } // console
    }

QUIT:
    return(0);
}/*}}}*/
